home *** CD-ROM | disk | FTP | other *** search
/ Software Vault: The Gold Collection / Software Vault - The Gold Collection (American Databankers) (1993).ISO / cdr22 / at_clock.zip / CLOCKERR.C next >
C/C++ Source or Header  |  1993-05-03  |  11KB  |  304 lines

  1.  
  2. /*                          clockerr.c
  3.  *
  4.  * This program determines the number of seconds lost or gained by  the
  5.  * cmos  clock  on  an AT computer. The program  is  invoked  from  the
  6.  * command line using one of two possible command line parameters, like
  7.  * this:  clockerr s, or clockerr q.
  8.  *
  9.  * The s parameter is used the  first time the program is run. The next
  10.  * time the program is run it must use the q parameter to generate  the
  11.  * cmos clock statistics. The first time you run the program, (clockerr
  12.  * s), you will be prompted for the correct date and time. Your entries
  13.  * will  be used to set the cmos clock. They will also be written to  a
  14.  * disk  file  called error.log. This file is created  in  the  current
  15.  * directory. If this file already exists then it means the cmos  clock
  16.  * has  already  been  set  by  this  program  and  further  action  is
  17.  * inhibited.  If you still want to reset the cmos clock then you  have
  18.  * to erase error.log first.
  19.  *
  20.  * The q parameter is used when you run this program anytime after  the
  21.  * first time. You will once again be prompted for the correct date and
  22.  * time.  These values will be used to compare the time as reported  by
  23.  * the  cmos clock to the actual time. The difference between the  cmos
  24.  * reported  time  and the actual time is adjusted to account  for  the
  25.  * length  of time the cmos clock has been running since it  was  first
  26.  * set by this program. The resulting value, seconds/month is  reported
  27.  * as a loss or gain, as appropriate.
  28.  *
  29.  * This program can be run with the q parameter anytime after the  cmos
  30.  * clock  has  been initially set by this program.  However,  the  cmos
  31.  * clock usually is not in error by more than a few minutes/month. This
  32.  * means  you will not obtain a particularly accurate value  unless  at
  33.  * least several days have passed.
  34.  */
  35.  
  36. #include <time.h>
  37. #include <stdlib.h>
  38. #include <stdio.h>
  39. #include <dos.h>
  40. #include <math.h>
  41.  
  42. #define   RT_CLOCK        0x1A
  43. #define   SET_RT_TIME     0x03
  44. #define   SET_RT_DATE     0x05
  45. #define   GET_SYS_TIME    0x2C
  46. #define   GET_SYS_DATE    0x2A
  47. #define   GET_RT_TIME     0x02
  48. #define   GET_RT_DATE     0x04
  49. #define   DOS_FUNC_21     0x21
  50. #define   MONTH           2419200.0   /* Number of seconds in 28 days */
  51.  
  52. void     main(int argc, char *argv[]);
  53. void     open_error_log(char *argv[]);
  54. int      bcd_to_bin(int bcd);
  55. int      bin_to_bcd(int bin);
  56. void     set_time(void);
  57. void     set_date(void);
  58. time_t   cmos_time(void);
  59.  
  60. FILE     *fp;
  61.  
  62. void main( int argc, char *argv[] )
  63.     {
  64.     time_t  start_time, elapsed_cmos, time_now, elapsed_actual, error, cmos;
  65.     struct tm  *dst;
  66.  
  67.     system("cls");
  68.     if(argc != 2)
  69.     {
  70.     puts("\nIncorrect usage: A command line parameter is needed.\n\n");
  71.     puts("Example:    First time program is run....clockerr s\n");
  72.     puts("            Subsequent times.............clockerr q\n");
  73.     exit(0);
  74.     }
  75.     if((*argv[1] != 's') && (*argv[1] != 'q'))
  76.     {
  77.     puts("\nInvalid parameter: Must be either s or q.");
  78.        exit(0);
  79.     }
  80.     open_error_log(argv);
  81.     switch(*argv[1])
  82.        {
  83.        case 's': /*
  84.           * Program is started for the first time.
  85.           *
  86.           * Set the system date and time. Convert this time to a
  87.           * value of type time_t. Call localtime() to determine if
  88.           * daylight savings time is in effect. If dst->tm_isdst = 1
  89.           * then subtract an hour to convert to standard time. Write
  90.           * the start_time to error.log and exit.
  91.           */
  92.          set_date();
  93.          set_time();
  94.          time(&start_time);
  95.          dst = localtime(&start_time);
  96.          if(dst->tm_isdst) start_time -= 3600;
  97.          fwrite(&start_time, sizeof(time_t), 1, fp);
  98.          puts("\nThe CMOS clock has been initialized.\n");
  99.          puts("The file, error.log has been created.\n");
  100.          exit(0);
  101.  
  102.        case 'q': /*
  103.           * Clock statistics are produced.
  104.           *
  105.           * Set the sysyem time and date correctly. The program
  106.           * will get the time from the operating system later.
  107.           */
  108.          puts("\n\nTHE UNCORRECTED TIME AND DATE ARE DISPLAYED.");
  109.          puts("ENTER THE CORRECT VALUES.\n");
  110.          system("date");
  111.          system("time");
  112.  
  113.          /*
  114.           * Get the time at which the program was originally
  115.           * started. This has been stored as a value of type
  116.           * time_t in the disc file, error.log.
  117.           */
  118.          fread(&start_time, sizeof(time_t), 1, fp);
  119.  
  120.          /*
  121.           * The current time as kept by the cmos clock is obtained
  122.           * from a call to cmos_time(). The number of seconds that
  123.           * have elapsed on the cmos clock is elapsed_cmos.
  124.           */
  125.          cmos = cmos_time();
  126.          elapsed_cmos = cmos - start_time;
  127.  
  128.          /*
  129.           * The correct time, time_now, is obtained from the system.
  130.           * A call to localtime() sets dst if daylight savings time
  131.           * is in effect. If dst = 1 then the time entered by the
  132.           * user is converted to standard time, which the program
  133.           * needs.
  134.           */
  135.          time(&time_now);
  136.          dst = localtime(&time_now);
  137.          if(dst->tm_isdst) time_now -= 3600;
  138.  
  139.          /*
  140.           * elapsed_actual is the amount of time that has passed
  141.           * since this program was first run, using the s parameter.
  142.           * The number of seconds by which the cmos clock varies
  143.           * from the actual elapsed time since then is computed
  144.           * and normalized to a per month value.
  145.           */
  146.          elapsed_actual = time_now - start_time;
  147.          error = floor((elapsed_cmos - elapsed_actual)*
  148.          MONTH/elapsed_actual +.5);
  149.          system("cls");
  150.          puts("\n\n---------------------- REAL TIME CLOCK STATISTICS ----------------------\n");
  151.          printf("    Correct time entered by user............%s\n",asctime(localtime(&time_now)));
  152.          printf("    Time reported by CMOS clock.............%s\n",asctime(localtime(&cmos)));
  153.          printf("    The CMOS clock was set on...............%s\n",asctime(localtime(&start_time)));
  154.          if(elapsed_cmos <= elapsed_actual)
  155.             printf("    The CMOS clock has lost.................%d seconds so far.\n\n", abs(elapsed_cmos - elapsed_actual));
  156.          else
  157.             printf("    The CMOS clock has gained...............%d seconds so far.\n\n", elapsed_cmos - elapsed_actual);
  158.          if(error <= 0)
  159.             printf("    The CMOS clock looses...................%d seconds/month.\n\n", abs(error));
  160.          else
  161.             printf("    The CMOS clock gains....................%d seconds/month.\n\n", error);
  162.          puts("------------------------------------------------------------------------");
  163.          exit(0);
  164.        }
  165.     }
  166.  
  167. void set_time()    /* Sets the system time then puts it in the cmos clock. */
  168.    {
  169.    union REGS  reg;
  170.    struct tm   *dst;
  171.    time_t      now;
  172.  
  173.    /*
  174.     * Set the system time. Setup for DOS call then return that time in REGS.
  175.     * Current time is also stored in now and passed to localtime() to see if
  176.     * daylight savings time is in effect. If so, then dst->tm_isdst = 1 and
  177.     * structure returned by the DOS call must be corrected by subtracting
  178.     * an hour (mod 24) from the field that represents the hour.
  179.     */
  180.    system("time");
  181.    reg.h.ah = GET_SYS_TIME;
  182.    int86(DOS_FUNC_21, ®, ®);
  183.    time(&now);
  184.    dst = localtime(&now);
  185.    if(dst->tm_isdst)
  186.       reg.h.ch = (reg.h.ch-1)<0 ? 23: reg.h.ch-1;
  187.  
  188.    /*
  189.     * Convert returned values to BCD. Set daylight savings time option to 0.
  190.     * Setup for and call BIOS with converted values to set the cmos clock.
  191.     */
  192.    reg.h.ch = bin_to_bcd(reg.h.ch);
  193.    reg.h.cl = bin_to_bcd(reg.h.cl);
  194.    reg.h.dh = bin_to_bcd(reg.h.dh);
  195.    reg.h.dl = 0;
  196.    reg.h.ah = SET_RT_TIME;
  197.    int86(RT_CLOCK, ®, ®);
  198.    }
  199.  
  200. void set_date()    /* Sets the system date then puts it in the cmos clock. */
  201.    {
  202.    union REGS   reg;
  203.    double   year, century;
  204.  
  205.    /*
  206.     * Set system date. Setup for DOS call then return that date in REGS.
  207.     */
  208.    system("date");
  209.    reg.h.ah = GET_SYS_DATE;
  210.    int86(DOS_FUNC_21, ®, ®);
  211.  
  212.    /*
  213.     * Separate century/year into century and year, (eg. 1988 to 1900, 88)
  214.     * then convert all returned values to BCD format, required by BIOS call.
  215.     */
  216.    year = reg.x.cx - 100*floor(.01*reg.x.cx);
  217.    century = (reg.x.cx - year);
  218.    reg.h.ch = bin_to_bcd((int)(.01*century));
  219.    reg.h.cl = bin_to_bcd((int)year);
  220.    reg.h.dh = bin_to_bcd(reg.h.dh);
  221.    reg.h.dl = bin_to_bcd(reg.h.dl);
  222.  
  223.    /*
  224.     * Setup for BIOS call. The date values obtained from the DOS call and
  225.     * converted to the proper format above are used to set the cmos clock.
  226.     */
  227.    reg.h.ah = SET_RT_DATE;
  228.    int86(RT_CLOCK, ®, ®);
  229.    }
  230.  
  231.  
  232. void open_error_log( char *argv[] )
  233.    {
  234.    switch(*argv[1])
  235.       {
  236.       case 's': /*
  237.          * Try to open a file with the r+ option. For this to be
  238.          * successful the file must already exist. If it does we
  239.          * probably don't want to overwrite it, hence the warning.
  240.          * If it doesn't exist, create it if possible.
  241.          */
  242.         if((fp = fopen("error.log", "r+")) != NULL)
  243.            {
  244.            puts("\nThe CMOS clock has been set already. If you");
  245.            puts("want to start over, erase error.log first.");
  246.            exit(0);
  247.            }
  248.         else
  249.            if((fp = fopen("error.log", "w")) == NULL)
  250.               {
  251.               puts("\nCan't open the required file.");
  252.               exit(0);
  253.               }
  254.            break;
  255.  
  256.       case 'q': /*
  257.          * The file error.log must exist to generate the clock
  258.          * statistics.
  259.          */
  260.         if((fp = fopen("error.log", "r")) == NULL)
  261.            {
  262.            puts("\nCan't open the error file, or it doesn't exist.");
  263.            puts("Probably, the CMOS clock was never initialized.");
  264.            exit(0);
  265.            }
  266.       }
  267.    }
  268.  
  269. time_t cmos_time()       /* Returns the elapsed time on the cmos clock. */
  270.    {
  271.    struct tm   cmos;
  272.    union REGS  reg;
  273.  
  274.    /*
  275.     * Setup for BIOS call. First get the time from the cmos clock, convert
  276.     * the result from BCD to binary format and place in the structure, cmos.
  277.     * Then do the same for the date. Finally call mktime() with a pointer to
  278.     * the structure cmos. This converts the structure to a representation of
  279.     * the time as a value of type time_t. This is the value returned.
  280.     */
  281.    reg.h.ah = GET_RT_TIME;
  282.    int86(RT_CLOCK, ®, ®);
  283.    cmos.tm_hour = bcd_to_bin(reg.h.ch);
  284.    cmos.tm_min =  bcd_to_bin(reg.h.cl);
  285.    cmos.tm_sec =  bcd_to_bin(reg.h.dh);
  286.    cmos.tm_isdst =0;
  287.    reg.h.ah = GET_RT_DATE;
  288.    int86(RT_CLOCK, ®, ®);
  289.    cmos.tm_year = bcd_to_bin(reg.h.ch)*100 + bcd_to_bin(reg.h.cl) - 1900;
  290.    cmos.tm_mon = bcd_to_bin(reg.h.dh) - 1;
  291.    cmos.tm_mday = bcd_to_bin(reg.h.dl);
  292.    return mktime(&cmos);
  293.    }
  294.  
  295. int bcd_to_bin( int bcd )         /* Convert from BCD format to binary. */
  296.    {
  297.    return  bcd - (bcd/16)*6;
  298.    }
  299.  
  300. int bin_to_bcd( int bin )         /* Convert from binary format to BCD. */
  301.    {
  302.    return  bin + (bin/10)*6;
  303.    }
  304.